/*
 * Thread.cpp
 *
 *  Created on: 2012-12-6
 *      Author: yangentao@gmail.com
 */

#include "Thread.h"
#include <iostream>
#include "StreamLog.h"
#include <exception>

using namespace std;

namespace yet {

DWORD WINAPI __MyThreadProc(LPVOID arg) {
	Thread* t = (Thread*) arg;
	int d = t->run(t->argp);
	return d;
}

Thread::Thread() {
	init();
}
Thread::Thread(boost::function<int (void* arg)> onRun) {
	init();
	OnRun = onRun;
}

Thread::~Thread() {
	if (threadHandle) {
		if (isAlive()) {
			assert(threadId != GetCurrentThreadId());
			slog << warn << "Thread is active when Thread Object destroy" << endln;
			waitFinish();
		}
		CloseHandle(threadHandle);
	}
}
void Thread::init() {
	threadHandle = 0;
	threadId = 0;
	argp = 0;
	priority = priorityNormal;
}

DWORD Thread::getThreadID() {
	return threadId;
}
bool Thread::start(){
	return start(0, false);
}
bool Thread::start(void* arg){
	return start(arg, false);
}
//if user call "ExitThread" in OnRun, thread handle will not close
//so, call WaitForSingleObject to test the ThreadHandler
bool Thread::start(void* arg, bool suspend) {
	if (isAlive()) {
		throw exception("thread is already runing when begin invoking.");
	}
	if (threadHandle) {
		CloseHandle(threadHandle);
	}
	init();
	this->argp = arg;
	DWORD flag = 0;
	if (suspend || priority != priorityNormal) {
		flag = CREATE_SUSPENDED;
	}
	threadHandle = CreateThread(0, 0, __MyThreadProc, this, CREATE_SUSPENDED, &threadId);
	if (threadHandle) {
		if (priority != priorityNormal) {
			SetThreadPriority(threadHandle, priority);
		}
		if (!suspend) {
			ResumeThread(threadHandle);
		}
	}
	return threadHandle != 0;
}

bool Thread::waitFinish(DWORD millSeconds) {
	if (!isAlive()) {
		return true;
	}

	if(inThread()){
		slog<<err<<"Invoking Thread::waitFinish in its own thread! "<<endln;
		return false;
	}
	DWORD n = WaitForSingleObject(threadHandle, millSeconds);
	switch (n) {
	case WAIT_FAILED : //fallthrough
	case WAIT_TIMEOUT:
		break;
	case WAIT_ABANDONED: //fallthrough
	case WAIT_OBJECT_0:
		return true;
		break;
	}
	return false;
}

int Thread::run(void* argp) {
	int n = 0;
	if (OnRun) {
		n = OnRun(argp);
	}
	if(OnRunReturn){
		OnRunReturn(argp, n);
	}
	return n;
}

//may be return STILL_ACTIVE indicate the thread is alive
bool Thread::getExitCode(DWORD& retcode) {
	if (threadHandle) {
		DWORD code = 0;
		if (GetExitCodeThread(threadHandle, &code)) {
			if (code == STILL_ACTIVE ) {
				return false;
			}else{
				retcode = code;
				return true;
			}
		}
	}
	return 0;
}

bool Thread::isAlive() {
	if (threadHandle) {
		DWORD code = 0;
		if (GetExitCodeThread(threadHandle, &code)) {
			if (code == STILL_ACTIVE ) {
				return true;
			}
		}
	}
	return false;

}

void Thread::setPriority(Priority p) {
	this->priority = priority;
	if (isAlive()) {
		if (!SetThreadPriority(threadHandle, priority)) {
			slog << err << "Set Thread Priority Failed" << endln;
		}
	}
}
void Thread::suspend() {
	if (isAlive()) {
		if (-1 == SuspendThread(threadHandle)) {
			slog << err << "SuspendThread Failed" << endln;
		}
	}
}
void Thread::resume() {
	if (isAlive()) {
		if (-1 == ResumeThread(threadHandle)) {
			slog << err << "ResumeThread Failed" << endln;
		}
	}
}

void Thread::postMessage(UINT msg, DWORD wParam, DWORD lParam){
	PostThreadMessage(threadId, msg, wParam, lParam);
}

bool Thread::inThread(){
	return threadId == GetCurrentThreadId();
}

} /* namespace yet */
